Personal.X-Istence.com

Bert JW Regeer (畢傑龍)

Moving from `mod_fastcgi` to `mod_fcgid`

Update: I've since moved to a PHP-FPM with mod_fastcgi based setup which works much better than mod_fcgid could ever dream of, taking process management away from Apache and putting it where it actually makes sense has improved server load and improved website speed. See my new PHP-FPM with mod_fastcgi post for more information.

--

Errors, all they cause is trouble. The dreaded 500 error showed up when visiting my favourite tech website, the one I am the administrator for. This time I had enough, there was going to be no more playing around with PHP settings, attempting to figure out why PHP was suddenly dying and or why mod_fastcgi refused to retry a select() when it failed due to a system signal.

The server in question runs Apache in MPM worker mode since a threaded Apache is going to be faster than a pre-fork, besides this server does not have as much memory so it seemed to be better to have multiple threads rather than multiple processes, each of which would have their own memory segment. There however is the issue that this server also needs to run PHP, the accepted method to do so is to use mod_php along with Apache; however PHP is not thread safe.

The alternative is FastCGI, basically it spawns PHP processes as a separate stand-alone process, with their own memory space, much like Apache pre-fork, however now when one process grows to big, or when a process is no longer needed it can be cleaned up, keeping memory to a minimum. Also, FastCGI is perfectly thread safe, this means that with Apache running in MPM worker mode we could now still run our PHP scripts even when they were not thread safe.

Setting up mod_fastcgi is not that hard, it takes some httpd.conf configuration values, and off course the loadmodule is assumed here:

# Set up mod_fastcgi
<IfModule `mod_fastcgi`.c>
    FastCgiIpcDir /var/tmp/fcgi-ipc/
    FastCgiConfig -autoUpdate -singleThreshold 100 -killInterval 300 -idle-timeout 240 -pass-header HTTP_AUTHORIZATION

    AddHandler  fastcgi-script              .fcgi .fcg .fpl

    Action      application/x-httpd-php5    /fastcgi-bin/php5.fcgi
    AddType     application/x-httpd-php5    .php .php5
</IfModule>

# Set up the script alias, basically anything in this directory gets executed as a fastcgi script
ScriptAlias /fastcgi-bin/ "/usr/local/www/fastcgi-bin/"
<Location /fastcgi-bin/>
    Options ExecCGI 
    SetHandler fastcgi-script
    Order allow,deny
    Allow from all
</Location>

Because of errors that mod_fastcgi was throwing out at me, I figured mod_fcgid is worth a try. However all of the configurations I found required me to add an FCGIWrapper line into each of my VirtualHost blocks, which was an immediate no-no since there is well over 100 of those on the server, and I'd rather spend my time doing other things.

With some trial and error, and some Googling I put the following together for mod_fcgid, tested it out, and once it worked perfectly set Apache off running with the following configuration:

# Set up mod_fcgid
<IfModule `mod_fcgid`.c>
    AddHandler  fcgid-script                 .fcgi .fcg .fpl
    IPCCommTimeout 60
    SocketPath  /var/tmp/fcgi-ipc/

    Action      application/x-httpd-php5    /fastcgi-bin/php5.fcgi
    AddType     application/x-httpd-php5    .php .php5
</IfModule>

# Set up the script alias, basically anything in this directory gets executed as a fastcgi script
ScriptAlias /fastcgi-bin/ "/usr/local/www/fastcgi-bin/"
<Location /fastcgi-bin/>
    Options ExecCGI 
    SetHandler fcgid-script
    Order allow,deny
    Allow from all
</Location>

Notice how similar they both are, that was the whole goal. mod_fcgid was supposed to be a drop in replacement, and thankfully it was. PHP was served as it once was, with a twist.

The php5.fcgi script is as follows for those of you trying to set this up as well:

1
2
3
4
5
6
7
#!/bin/sh
# To use your own php.ini, comment the next line and uncomment the following one
#PHPRC="/usr/local/etc"
#export PHPRC
PHP_FCGI_CHILDREN=4
export PHP_FCGI_CHILDREN
exec /usr/local/bin/php-cgi

Some things that that intrigued me is that my php processes seemed to be capped now. Where mod_fastcgi would spawn processes but never remove them or kill them when they were no longer needed, mod_fcgid keeps the running php processes to a sane limit instantly starting new processes when required to handle the requests coming in. This means that the server now has more free memory for MySQL, or file caches which has helped speed up other types of transfers as well. Even with the relatively short time that mod_fcgid has been in place it has been faster, more reliable and more sane than mod_fastcgi. Whichever one you pick, make sure to benchmark your server and check your error logs to solve common issues, the issue may not be what FastCGI module you picked but rather PHP itself that is causing the errors!